{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 7.Adaboost算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from numpy import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.1.单层决策树\n",
    "### 7.1.1.读取数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def loadSimpData():\n",
    "    \"\"\"\n",
    "    读取简单数据\n",
    "    参数：\n",
    "        无\n",
    "    返回：\n",
    "        dataMat -- 数据矩阵\n",
    "        classLabels -- 标签列表\n",
    "    \"\"\"\n",
    "    #简单地生成数据和标签\n",
    "    datMat = matrix([[ 1. ,  2.1],\n",
    "        [ 2. ,  1.1],\n",
    "        [ 1.3,  1. ],\n",
    "        [ 1. ,  1. ],\n",
    "        [ 2. ,  1. ]])\n",
    "    classLabels = [1.0, 1.0, -1.0, -1.0, 1.0]\n",
    "    return datMat,classLabels"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.1.2.建立单层决策树（树桩）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def stumpClassify(dataMatrix,dimen,threshVal,threshIneq):\n",
    "    \"\"\"\n",
    "    分类数据\n",
    "    参数：\n",
    "        dataMatrix -- 数据矩阵\n",
    "        dimen -- 维度\n",
    "        threshVal -- 阈值\n",
    "        threshIneq -- 符号，‘lt’小于，‘gt’大于\n",
    "    返回：\n",
    "        retArray -- 返回矩阵\n",
    "    \"\"\"\n",
    "    #新建矩阵，初始化为1\n",
    "    retArray = ones((shape(dataMatrix)[0],1))\n",
    "    #判断符号\n",
    "    if threshIneq == 'lt':\n",
    "        retArray[dataMatrix[:,dimen] <= threshVal] = -1.0\n",
    "    else:\n",
    "        retArray[dataMatrix[:,dimen] > threshVal] = -1.0\n",
    "    return retArray\n",
    "    \n",
    "\n",
    "def buildStump(dataArr,classLabels,D):\n",
    "    \"\"\"\n",
    "    建立单层决策树\n",
    "    参数：\n",
    "        dataArr -- 数据矩阵\n",
    "        classLabels -- 类标签\n",
    "        D -- 权重向量\n",
    "    返回：\n",
    "        bestStump -- 最佳树桩\n",
    "        minError -- 最小误差\n",
    "        bestClassEst -- 最佳分类估计\n",
    "    \"\"\"\n",
    "    #将数据和标签转化为矩阵\n",
    "    dataMatrix = mat(dataArr); labelMat = mat(classLabels).T\n",
    "    #矩阵维度\n",
    "    m,n = shape(dataMatrix)\n",
    "    #设置和初始化一些量\n",
    "    numSteps = 10.0; bestStump = {}; bestClasEst = mat(zeros((m,1)))\n",
    "    #初始最小误差为无穷大\n",
    "    minError = inf\n",
    "    #遍历所有特征\n",
    "    for i in range(n):\n",
    "        #获得该特征值的范围\n",
    "        rangeMin = dataMatrix[:,i].min(); rangeMax = dataMatrix[:,i].max();\n",
    "        #步长\n",
    "        stepSize = (rangeMax-rangeMin)/numSteps\n",
    "        #遍历所有范围步长\n",
    "        for j in range(-1,int(numSteps)+1):\n",
    "            #对于小于和大于两种情况\n",
    "            for inequal in ['lt', 'gt']: \n",
    "                #阈值\n",
    "                threshVal = (rangeMin + float(j) * stepSize)\n",
    "                #分类预测\n",
    "                predictedVals = stumpClassify(dataMatrix,i,threshVal,inequal)\n",
    "                #错误率矩阵初始化为全1\n",
    "                errArr = mat(ones((m,1)))\n",
    "                #将分类正确的对应错误标记设置为0，正确\n",
    "                errArr[predictedVals == labelMat] = 0\n",
    "                #加权后的总错误率\n",
    "                weightedError = D.T*errArr\n",
    "                #print(\"split: dim %d, thresh %.2f, thresh ineqal: %s, the weighted error is %.3f\" % (i, threshVal, inequal, weightedError))\n",
    "                #记录最好结果相关信息\n",
    "                if weightedError < minError:\n",
    "                    minError = weightedError\n",
    "                    bestClasEst = predictedVals.copy()\n",
    "                    bestStump['dim'] = i\n",
    "                    bestStump['thresh'] = threshVal\n",
    "                    bestStump['ineq'] = inequal\n",
    "    return bestStump,minError,bestClasEst"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "测试一下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "D = mat(ones((5, 1)) / 5)\n",
    "datMat,classLabels = loadSimpData()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "({'dim': 0, 'thresh': 1.3, 'ineq': 'lt'}, matrix([[0.2]]), array([[-1.],\n",
       "        [ 1.],\n",
       "        [-1.],\n",
       "        [-1.],\n",
       "        [ 1.]]))"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "buildStump(datMat,classLabels,D)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.2.完整Adaboost算法实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def adaBoostTrainDS(dataArr,classLabels,numIt=40):\n",
    "    \"\"\"\n",
    "    Adaboost的训练函数\n",
    "    参数：\n",
    "        dataArr -- 数据矩阵\n",
    "        classLabels -- 分类标签\n",
    "        numIt -- 迭代次数\n",
    "    返回：\n",
    "        weakClassArr -- 弱分类器\n",
    "        aggClassEst -- 类估计\n",
    "    \"\"\"\n",
    "    weakClassArr = []\n",
    "    m = shape(dataArr)[0]\n",
    "    #相同的初始权重\n",
    "    D = mat(ones((m,1))/m)\n",
    "    #初始估计为全0\n",
    "    aggClassEst = mat(zeros((m,1)))\n",
    "    #在最大迭代次数限制下\n",
    "    for i in range(numIt):\n",
    "        #建立弱分类器，树桩\n",
    "        bestStump,error,classEst = buildStump(dataArr,classLabels,D)\n",
    "        #print(\"D:\",D.T)\n",
    "        #分类器权重，除法时保证不除0\n",
    "        alpha = float(0.5*log((1.0-error)/max(error,1e-16)))\n",
    "        bestStump['alpha'] = alpha  \n",
    "        #保存树桩\n",
    "        weakClassArr.append(bestStump)\n",
    "        #print(\"classEst: \",classEst.T)\n",
    "        #计算指数项\n",
    "        expon = multiply(-1*alpha*mat(classLabels).T,classEst)\n",
    "        #更新D\n",
    "        D = multiply(D,exp(expon))\n",
    "        D = D/D.sum()\n",
    "        #结合弱分类器\n",
    "        aggClassEst += alpha*classEst\n",
    "        #print(\"aggClassEst: \",aggClassEst.T)\n",
    "        #误差\n",
    "        aggErrors = multiply(sign(aggClassEst) != mat(classLabels).T,ones((m,1)))\n",
    "        errorRate = aggErrors.sum()/m\n",
    "        print(\"total error: \",errorRate)\n",
    "        #如果误差为0，提前结束\n",
    "        if errorRate == 0.0: break\n",
    "    return weakClassArr,aggClassEst"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "测试一下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "total error:  0.2\n",
      "total error:  0.2\n",
      "total error:  0.0\n"
     ]
    }
   ],
   "source": [
    "classifierArray,_ = adaBoostTrainDS(datMat,classLabels,numIt=40)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.3.测试算法：基于AdaBoost 的分类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def adaClassify(datToClass,classifierArr):\n",
    "    \"\"\"\n",
    "    分类函数\n",
    "    参数：\n",
    "        datToClass -- 数据\n",
    "        classifierArr -- 分类器\n",
    "    返回：\n",
    "        aggClassEst -- 预测值\n",
    "    \"\"\"\n",
    "    dataMatrix = mat(datToClass)\n",
    "    m = shape(dataMatrix)[0]\n",
    "    aggClassEst = mat(zeros((m,1)))\n",
    "    #带权重的分类结果\n",
    "    for i in range(len(classifierArr)):\n",
    "        classEst = stumpClassify(dataMatrix,classifierArr[i]['dim'],\\\n",
    "                                 classifierArr[i]['thresh'],\\\n",
    "                                 classifierArr[i]['ineq'])#call stump classify\n",
    "        aggClassEst += classifierArr[i]['alpha']*classEst\n",
    "        #print(aggClassEst)\n",
    "    return sign(aggClassEst)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plotROC(predStrengths, classLabels):\n",
    "    \"\"\"\n",
    "    绘制ROC图\n",
    "    参数：\n",
    "        predStrengths -- 预测\n",
    "        classLabels -- 类\n",
    "    \"\"\"\n",
    "    import matplotlib.pyplot as plt\n",
    "    #cursor\n",
    "    cur = (1.0,1.0)\n",
    "    #求和器\n",
    "    ySum = 0.0\n",
    "    numPosClas = sum(array(classLabels)==1.0)\n",
    "    yStep = 1/float(numPosClas); xStep = 1/float(len(classLabels)-numPosClas)\n",
    "    #排序\n",
    "    sortedIndicies = predStrengths.argsort()\n",
    "    fig = plt.figure()\n",
    "    fig.clf()\n",
    "    ax = plt.subplot(111)\n",
    "    #每个点画一个线段\n",
    "    for index in sortedIndicies.tolist()[0]:\n",
    "        if classLabels[index] == 1.0:\n",
    "            delX = 0; delY = yStep;\n",
    "        else:\n",
    "            delX = xStep; delY = 0;\n",
    "            ySum += cur[1]\n",
    "        ax.plot([cur[0],cur[0]-delX],[cur[1],cur[1]-delY], c='b')\n",
    "        cur = (cur[0]-delX,cur[1]-delY)\n",
    "    ax.plot([0,1],[0,1],'b--')\n",
    "    plt.xlabel('False positive rate'); plt.ylabel('True positive rate')\n",
    "    plt.title('ROC curve for AdaBoost horse colic detection system')\n",
    "    ax.axis([0,1,0,1])\n",
    "    #plt.show()\n",
    "    print(\"the Area Under the Curve is: \",ySum*xStep)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'dim': 0, 'thresh': 1.3, 'ineq': 'lt', 'alpha': 0.6931471805599453}, {'dim': 1, 'thresh': 1.0, 'ineq': 'lt', 'alpha': 0.9729550745276565}, {'dim': 0, 'thresh': 0.9, 'ineq': 'lt', 'alpha': 0.8958797346140273}]\n"
     ]
    }
   ],
   "source": [
    "print(classifierArray)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "matrix([[-1.]])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "adaClassify([0, 0], classifierArray)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.4.在一个难数据集上应用AdaBoost\n",
    "### 7.4.1.读取数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def loadDataSet(fileName):\n",
    "    \"\"\"\n",
    "    读取数据集\n",
    "    参数：\n",
    "        fileName -- 文件名\n",
    "    返回：\n",
    "        dataMat -- 数据矩阵\n",
    "        labelMat -- 标签矩阵\n",
    "    \"\"\"\n",
    "    #读取特征数，以制表符分割\n",
    "    numFeat = len(open(fileName).readline().split('\\t'))\n",
    "    #新建矩阵\n",
    "    dataMat = []; labelMat = []\n",
    "    #打开文件\n",
    "    fr = open(fileName)\n",
    "    #按行读取\n",
    "    for line in fr.readlines():\n",
    "        lineArr =[]\n",
    "        #以制表符分割\n",
    "        curLine = line.strip().split('\\t')\n",
    "        #遍历每个特征\n",
    "        for i in range(numFeat-1):\n",
    "            lineArr.append(float(curLine[i]))\n",
    "        dataMat.append(lineArr)\n",
    "        #最后一项是标签，添加到标签矩阵中\n",
    "        labelMat.append(float(curLine[-1]))\n",
    "    return dataMat,labelMat"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "total error:  0.2842809364548495\n",
      "total error:  0.2842809364548495\n",
      "total error:  0.24749163879598662\n",
      "total error:  0.24749163879598662\n",
      "total error:  0.25418060200668896\n",
      "total error:  0.2408026755852843\n",
      "total error:  0.2408026755852843\n",
      "total error:  0.22073578595317725\n",
      "total error:  0.24749163879598662\n",
      "total error:  0.23076923076923078\n"
     ]
    }
   ],
   "source": [
    "datArr, labelArr = loadDataSet(\"horseColicTraining2.txt\")\n",
    "classifierArray,_ = adaBoostTrainDS(datArr, labelArr, 10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "错误率 = 23.88%\n"
     ]
    }
   ],
   "source": [
    "testArr, testLabelArr = loadDataSet(\"horseColicTest2.txt\")\n",
    "prediction10 = adaClassify(testArr, classifierArray)\n",
    "errArr = mat(ones((67, 1)))\n",
    "errRate = errArr[prediction10 != mat(testLabelArr).T].sum() / 67 * 100\n",
    "print(f\"错误率 = {errRate:2.2f}%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "使用50个弱分类器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "total error:  0.2842809364548495\n",
      "total error:  0.2842809364548495\n",
      "total error:  0.24749163879598662\n",
      "total error:  0.24749163879598662\n",
      "total error:  0.25418060200668896\n",
      "total error:  0.2408026755852843\n",
      "total error:  0.2408026755852843\n",
      "total error:  0.22073578595317725\n",
      "total error:  0.24749163879598662\n",
      "total error:  0.23076923076923078\n",
      "total error:  0.2408026755852843\n",
      "total error:  0.2140468227424749\n",
      "total error:  0.22742474916387959\n",
      "total error:  0.21739130434782608\n",
      "total error:  0.22073578595317725\n",
      "total error:  0.21739130434782608\n",
      "total error:  0.22408026755852842\n",
      "total error:  0.22408026755852842\n",
      "total error:  0.23076923076923078\n",
      "total error:  0.22408026755852842\n",
      "total error:  0.2140468227424749\n",
      "total error:  0.20735785953177258\n",
      "total error:  0.22408026755852842\n",
      "total error:  0.22408026755852842\n",
      "total error:  0.2140468227424749\n",
      "total error:  0.22073578595317725\n",
      "total error:  0.2040133779264214\n",
      "total error:  0.20735785953177258\n",
      "total error:  0.21070234113712374\n",
      "total error:  0.21739130434782608\n",
      "total error:  0.21070234113712374\n",
      "total error:  0.21739130434782608\n",
      "total error:  0.20735785953177258\n",
      "total error:  0.21070234113712374\n",
      "total error:  0.20735785953177258\n",
      "total error:  0.20735785953177258\n",
      "total error:  0.19732441471571907\n",
      "total error:  0.19063545150501673\n",
      "total error:  0.20066889632107024\n",
      "total error:  0.19732441471571907\n",
      "total error:  0.20066889632107024\n",
      "total error:  0.1939799331103679\n",
      "total error:  0.1939799331103679\n",
      "total error:  0.19063545150501673\n",
      "total error:  0.18729096989966554\n",
      "total error:  0.19063545150501673\n",
      "total error:  0.19063545150501673\n",
      "total error:  0.18729096989966554\n",
      "total error:  0.1939799331103679\n",
      "total error:  0.18729096989966554\n"
     ]
    }
   ],
   "source": [
    "classifierArray, aggClassEst = adaBoostTrainDS(datArr, labelArr, 50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "错误率 = 20.90%\n"
     ]
    }
   ],
   "source": [
    "testArr, testLabelArr = loadDataSet(\"horseColicTest2.txt\")\n",
    "prediction10 = adaClassify(testArr, classifierArray)\n",
    "errArr = mat(ones((67, 1)))\n",
    "errRate = errArr[prediction10 != mat(testLabelArr).T].sum() / 67 * 100\n",
    "print(f\"错误率 = {errRate:2.2f}%\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "the Area Under the Curve is:  0.8953941870182941\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plotROC(aggClassEst.T, labelArr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
